// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2022 Ivan Baidakou

#pragma once

#include "actor.hpp"
#include "message.hpp"
#include "queue.hpp"
#include <tuple>
#include <type_traits>
#include <utility>

namespace rotor_light {

struct QueueBase;
struct PlannerBase;

namespace details {
struct ChildState {
  State down_state = State::off;
};
} // namespace details

struct SupervisorBase : ActorBase {
  static constexpr size_t min_handlers_amount =
      ActorBase::min_handlers_amount + 2;
  friend struct ActorBase;
  using Parent = ActorBase;

  SupervisorBase(char *backends, size_t backends_count, ActorBase **actors,
                 details::ChildState *states, size_t actors_count);

  inline QueueBase *get_queue() { return queue; }

  void initialize() override;
  void start(bool poll_timer = false);

  virtual void bind(Context &context, ActorId value = 0);
  uint8_t bind(ActorId initial_value, SupervisorBase *supervisor,
               Context &context) override;
  void process();
  void dispatch(Message &, MessageTypeId message_type_id);

  // handlers
  void advance_init() override;
  void advance_start() override;
  void advance_stop() override;

  inline PlannerBase *get_planner() { return planner; }

protected:
  void on_state_change_ack(message::ChangeStateAck &);
  void on_child_init_failure(size_t actor_index);
  virtual void on_refhesh_timer(message::RefreshTime &);
  void on_child_down(size_t actor_index);
  void init_child(size_t actor_index);
  void start_actor(size_t actor_index);
  size_t process_watchdog();

  bool check_child_state(State subject, bool skip_self);

  ActorBase **actors;
  details::ChildState *states;
  size_t actors_count;
  QueueBase *queue;
  PlannerBase *planner;
  NowFunction now;
};

template <size_t HandlersCount, typename... Actors>
struct Supervisor : SupervisorBase {
  static_assert(HandlersCount >= SupervisorBase::min_handlers_amount,
                "no enough handlers");

  Supervisor()
      : SupervisorBase(reinterpret_cast<char *>(&backends), HandlersCount,
                       actors, children_states_holder, children_count + 1) {
    actors[0] = this;
    fill_actor<0>();
  }

  template <size_t ChildIndex> auto get_child() {
    return &std::get<ChildIndex>(children);
  }

protected:
  template <size_t ChildIndex> void fill_actor() {
    if constexpr (ChildIndex < children_count) {
      actors[ChildIndex + 1] = get_child<ChildIndex>();
      fill_actor<ChildIndex + 1>();
    }
  }

  using ActorsList = std::tuple<Actors...>;
  static constexpr size_t children_count = std::tuple_size_v<ActorsList>;
  ActorsList children;
  ActorBase::Storage backends[HandlersCount];
  ActorBase *actors[children_count + 1];
  details::ChildState children_states_holder[children_count + 1];
};

template <typename MessageType, typename... Args>
bool ActorBase::send(size_t queue_index, Args... args) {
  auto &queue = supervisor->queue;
  return queue->put<MessageType>(queue_index, std::forward<Args>(args)...);
}

} // namespace rotor_light
